/*
 * (c) 2012 Leonid Bensman, lbensman.cs00@gtalumni.org
 */
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Threading;
using System.Collections;

namespace CalcSig
{
    public class ItemHash
    {
        public object SourceData;
        public virtual string Path
        {
            get
            {
                return (SourceData is string) ? SourceData as string : string.Empty;
            }
        }

        private long? _fileSize = null;
        public virtual long FileSize
        {
            get
            {
                if (this._fileSize.HasValue)
                {
                    return this._fileSize.Value;
                }
                else if (this.SourceData is string)
                {
                    // Assume this is path
                    try
                    {
                        FileInfo fi = new FileInfo(this.SourceData as string);

                        return fi.Length;
                    }
                    catch (Exception)
                    {
                    }
                }

                return -1;
            }
            set
            {
                this._fileSize = value;
            }
        }
    }

    public delegate void ItemCalculatedDelegate(ItemHash itemHash);

    public abstract class HashCalculator<ItemExtendedHash, JobDescriptorType> : IHashCalculator
        where ItemExtendedHash : ItemHash, new()
    {
        protected readonly IEnumerable<JobDescriptorType> enumerableJob;

        public event ItemCalculatedDelegate ItemCalculated;

        protected HashCalculator(IEnumerable<JobDescriptorType> enumerableJob)
        {
            this.enumerableJob = enumerableJob;
        }

        public void CalculateHashes()
        {
            CalculateHashes(Environment.ProcessorCount);
        }

        private void CalculateHashes(int workerThreadCount)
        {
            if (workerThreadCount < 1)
            {
                throw new ArgumentOutOfRangeException("workerThreadCount");
            }

            IEnumerator<JobDescriptorType> jobQueue = enumerableJob.GetEnumerator();

            if (workerThreadCount == 1)
            {
                CalculateHashes(jobQueue);
            }
            else
            {
                Thread[] workerThreads = new Thread[workerThreadCount];

                for (int i = 0; i < workerThreadCount - 1; i++)
                {
                    workerThreads[i] = new Thread(new ParameterizedThreadStart(CalculateHashes));
                    workerThreads[i].Start(jobQueue);
                }

                CalculateHashes(jobQueue);

                for (int i = 0; i < workerThreadCount - 1; i++)
                {
                    workerThreads[i].Join();
                }
            }
        }

        private void CalculateHashes(object state)
        {
            IEnumerator<JobDescriptorType> jobQueue = state as IEnumerator<JobDescriptorType>;

            if (jobQueue == null)
            {
                Console.Error.WriteLine("state for worker thread is not specified, or is not a jobQueue.  Process will now terminate.");
                System.Diagnostics.Process.GetCurrentProcess().Kill();
            }

            JobDescriptorType jobDescriptor;

            while (true)
            {
                lock (jobQueue)
                {
                    if (!jobQueue.MoveNext())
                    {
                        // This means the end of queue
                        return;
                    }

                    jobDescriptor = jobQueue.Current;
                }

                CalculateFileHash(jobDescriptor);
            }
        }

        private void CalculateFileHash(JobDescriptorType jobDescriptor)
        {
            ItemExtendedHash itemHash = new ItemExtendedHash();

            itemHash.SourceData = jobDescriptor;

            OnCalculateFileHash(itemHash);
        }

        protected virtual void OnCalculateFileHash(ItemExtendedHash itemHash)
        {
            OnItemCalculated(itemHash);
        }

        private void OnItemCalculated(ItemExtendedHash itemHash)
        {
            if (ItemCalculated != null)
            {
                ItemCalculated(itemHash);
            }
        }

        public abstract JobStats Estimate();
    }

    public abstract class FileContentHashCalculator<ItemExtendedHash> : HashCalculator<ItemExtendedHash, string>
        where ItemExtendedHash : ItemHash, new()
    {
        public FileContentHashCalculator(IEnumerable<string> enumerableJob)
            : base(enumerableJob)
        {
        }

        public override JobStats Estimate()
        {
            JobStats estimate = new JobStats();

            foreach (string file in enumerableJob)
            {
                FileInfo fi = new FileInfo(file);

                estimate.AddFileToEstimates(fi);
            }

            return estimate;
        }
    }

}
